package net.fdukedom.epicurus.tools.logging;

import net.fdukedom.epicurus.domain.entity.LogEntry;
import net.fdukedom.epicurus.domain.util.LogHelper;
import net.fdukedom.epicurus.tools.config.Configuration;
import net.fdukedom.epicurus.tools.config.Key;

import java.util.Map;
import java.util.HashMap;

/**
 * Logger class. All log operation are to be done through it.
 * @author Dmitry Y. Kochelaev
 */
public class Logger {

    /**
     * Instance of class.
     */
    private static Logger defaultInstance;

    /**
     * Pool of instances for writers.
     */
    private static Map<String, Logger> loggers = new HashMap<String, Logger>();

    /**
     * Default writerId.
     */
    private static final String DEFAULT_WRITER = "default";

    /**
     * Log status.
     */
    private static Status logStatus = Status.valueOf(Configuration.getInstance().getStringValue(Key.LOG_LEVEL));

    /**
     * Logger writerId.
     */
    private final String writerId;

    /**
     * Utility class.
     * 
     * @param writerId writerId
     */
    private Logger(String writerId) {
        this.writerId = writerId;
    }

    /**
     * Returns instance of the class.
     *
     * @return instance of the class
     */
    public static Logger getInstance() {
        if (defaultInstance == null) {
            defaultInstance = new Logger(DEFAULT_WRITER);
        }
        return defaultInstance;
    }

    /**
     * Returns logger for specified writer.
     *
     * @param writerId writerId
     *
     * @return logger for specified writer
     */
    public static Logger getInstance(String writerId) {
        if (!loggers.containsKey(writerId)) {
            loggers.put(writerId, new Logger(writerId));
        }
        return loggers.get(writerId);
    }

    /**
     * Returns logger for specified writer.
     *
     * @param writerId writerId
     *
     * @return logger for specified writer
     */
    public static Logger getInstance(WriterId writerId) {
        return Logger.getInstance(writerId.getValue());
    }

    /**
     * Logs an event.
     *
     * @param writerId writer of log entry
     * @param status event status
     * @param message message to be save in log
     */
    public void log(String writerId, Status status, String message) {
        if (status.getStatus() < logStatus.getStatus()) {
            return;
        }
        LogEntry entry = new LogEntry(writerId, status.getValue(), message);
        LogHelper.getInstance().saveLogEntry(entry);
    }

    /**
     * Logs an event.
     *
     * @param status event status
     * @param message message to be save in log
     */
    public void log(Status status, String message) {
        log(writerId, status, message);
    }

    /**
     * Logs a debug event.
     *
     * @param message message to be save in log
     */
    public void logDebug(String message) {
        log(writerId, Status.DEBUG, message);
    }

    /**
     * Logs an info event.
     *
     * @param message message to be save in log
     */
    public void logInfo(String message) {
        log(writerId, Status.INFO, message);
    }

    /**
     * Logs a warning event.
     *
     * @param message message to be save in log
     */
    public void logWarning(String message) {
        log(writerId, Status.WARN, message);
    }

    /**
     * Logs an error event.
     *
     * @param message message to be save in log
     */
    public void logError(String message) {
        log(writerId, Status.ERROR, message);
    }

    /**
     * Logs an fatal event.
     *
     * @param message message to be save in log
     */
    public void logFatal(String message) {
        log(writerId, Status.FATAL, message);
    }

    /**
     * Returns writerId.
     *
     * @return writerId
     */
    public String getWriterId() {
        return writerId;
    }

    /**
     * Returns log status.
     *
     * @return log status
     */
    public static Status getLogStatus() {
        return logStatus;
    }

    /**
     * Returns if specified status is enabled.
     *
     * @param status status to be checked
     *
     * @return if specified status is enabled
     */
    public boolean isStatusEnabled(Status status) {
        return new StatusComparator().compare(status, logStatus) >= 0;
    }

    /**
     * Returns if debug is enabled.
     *
     * @return true if debug is enabled and false otherwise
     */
    public boolean isDebugEnabled() {
        return isStatusEnabled(Status.DEBUG);
    }

    /**
     * Returns if info is enabled.
     *
     * @return true if info is enabled and false otherwise
     */
    public boolean isInfoEnabled() {
        return isStatusEnabled(Status.INFO);
    }

    /**
     * Returns if warn is enabled.
     *
     * @return true if warn is enabled and false otherwise
     */
    public boolean isWarnEnabled() {
        return isStatusEnabled(Status.WARN);
    }

    /**
     * Returns if error is enabled.
     *
     * @return true if error is enabled and false otherwise
     */
    public boolean isErrorEnabled() {
        return isStatusEnabled(Status.ERROR);
    }

    /**
     * Returns if fatal is enabled.
     *
     * @return true if fatal is enabled and false otherwise
     */
    public boolean isFatalEnabled() {
        return isStatusEnabled(Status.FATAL);
    }
}
